<script setup lang="ts">
/**
 * MapRouteDrawer 地图绘线组件（解耦版）
 * ————————————————————————————————————————————————
 * 职责：
 *  1) 在 Mapbox 地图上绘制/编辑自定义航线（点击新增 → 移动预览 → 双击结束 → 拖拽点位）
 *  2) 仅负责点位(latitude/longitude)与折线渲染；不包含区域限制、业务表单、任务态等逻辑
 *  3) 通过 v-model:points 与外部数据双向绑定
 *
 * 设计要点：
 *  - props 使用 withDefaults 设置默认值，避免修改 props 的反模式
 *  - 统一地图样式就绪判断 whenStyleReady，源/图层的添加具备幂等性
 *  - 跨日界线处理：unwrap → 分段 → MultiLineString
 *  - 事件/资源清理集中在 cleanup()，单一 onBeforeUnmount 保证退出安全
 *  - 拖拽事件合并为 onDragUpdate，避免重复注册 dragend 处理
 */

import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import type { Map as MapboxMap, Marker } from 'mapbox-gl'
import { useWeatherStore } from '@/store/weather'
// 运行时导入，确保在 ESM 项目中也能拿到 Marker 构造函数
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import * as mapboxgl from 'mapbox-gl'
import {ElTable} from "element-plus";

/* ================================
 * 类型定义
 * ============================== */
export interface RoutePoint {
  isPilot?: boolean
  latitude: number
  longitude: number
}

/* ================================
 * Props & Emits（带默认值）
 * ============================== */
const props = withDefaults(defineProps<{
  /** v-model:points */
  points: RoutePoint[]
  /** 是否允许交互绘制/编辑 */
  editable?: boolean
  /** 标记是否可拖拽 */
  markerDraggable?: boolean
  /** 线条图层/数据源 ID，可覆盖 */
  lineSourceId?: string
  lineLayerId?: string
}>(), {
  editable: true,
  markerDraggable: true,
  lineSourceId: 'edit-route-src',
  lineLayerId: 'edit-route-layer'
})

const emit = defineEmits<{
  (e: 'update:points', v: RoutePoint[]): void
  (e: 'start'): void
  (e: 'change', v: RoutePoint[]): void
  (e: 'complete', v: RoutePoint[]): void
  (e: 'cancel'): void
  (e: 'select', i: number | null): void
}>()

/* ================================
 * 本地状态
 * ============================== */
let map: MapboxMap
const drawing = ref(false)                 // 是否处于“绘制中”
const markers = ref<Marker[]>([])          // 地图 Marker 引用
const isDragging = ref(false)                 // 是否处于拖拽中，拖拽中禁止全量重绘
const selectedIndex = ref<number | null>(null) // 表格/地图当前选中点
const localPoints = ref<RoutePoint[]>(normalize(props.points))
// 防抖：标记“正从 props 同步到本地”，避免 v-model 回写形成递归
const syncingFromProps = ref(false)

// 仅在关键时机（拖拽结束/绘制完成）同步到外部，减少渲染压力
const CHANGE_DEBOUNCE_MS = 150
let changeTimer: ReturnType<typeof setTimeout> | null = null
function emitPointsDebounced() {
  if (changeTimer) clearTimeout(changeTimer)
  changeTimer = setTimeout(() => {
    const pts = localPoints.value.map(p => ({ ...p }))
    emit('update:points', pts)
    emit('change', pts)
  }, CHANGE_DEBOUNCE_MS)
}

// props → 本地点位
watch(() => props.points, (nv) => {
  console.log('watch:points', nv)
  syncingFromProps.value = true
  localPoints.value = normalize(nv || [])
  redraw()
  nextTick(() => { syncingFromProps.value = false })
}, { deep: true })

// 本地点位改变时仅做选中索引兜底，不主动向外同步（减少高频回写）
watch(localPoints, (nv) => {
  if (selectedIndex.value != null && selectedIndex.value >= nv.length) {
    selectedIndex.value = nv.length ? nv.length - 1 : null
  }
}, { deep: true })

/* ================================
 * 生命周期：挂载/卸载
 * ============================== */
onMounted(() => {
  const weatherStore = useWeatherStore()
  // 兼容 pinia state/refs
  // @ts-ignore
  map = (weatherStore as any).map as MapboxMap
  if (!map) {
    console.warn('[custom-route] map is not ready from useWeatherStore.')
  }
  // 地图引用变化时，重新初始化资源
  watch(() => (weatherStore as any).map, (m) => {
    if (!m) return
    // @ts-ignore
    map = m as MapboxMap
    whenStyleReady(() => { ensureLineLayer(); redraw() })
  })
  whenStyleReady(() => { ensureLineLayer(); redraw() })
})

onBeforeUnmount(() => {
  cleanup()
  clearAll()
  unbindCtxOutside()
})

/* ================================
 * 地图样式/构造 工具方法
 * ============================== */
// 统一获取 Marker 构造函数（优先全局 window.mapboxgl，其次 ESM 导入）
function getMarkerCtor(): any {
  // @ts-ignore
  const g = (typeof window !== 'undefined' ? (window as any).mapboxgl : undefined)
  if (g && g.Marker) return g.Marker
  // @ts-ignore
  if ((mapboxgl as any)?.Marker) return (mapboxgl as any).Marker
  console.warn('[custom-route] Mapbox Marker constructor not found.')
  return null
}

// 样式就绪后执行回调（源/图层依赖样式）
function whenStyleReady(cb: () => void) {
  if (!map) return
  try {
    if (map.isStyleLoaded && map.isStyleLoaded()) { cb(); return }
  } catch {}
  const once = () => { try { map.off('load', once) } catch {}; try { map.off('styledata', once) } catch {}; cb() }
  try { map.on('load', once) } catch {}
  try { map.on('styledata', once) } catch {}
}

/* ================================
 * 对外暴露：绘制/编辑控制
 * ============================== */
function startDraw() {
  if (!props.editable || drawing.value) return
  drawing.value = true
  bindMapEvents()
  // 禁用地图双击缩放，避免吞掉我们用于结束绘制的 dblclick
  try {
    // @ts-ignore
    map?.doubleClickZoom?.disable?.()
  } catch {}
  emit('start')
  // 若已有点，追加占位点用于“移动预览”
  if (localPoints.value.length > 0) {
    localPoints.value.push({ ...clonePoint(localPoints.value[localPoints.value.length - 1]) })
  }
}

function stopDraw(commit = true) {
  if (!drawing.value) return
  // 恢复双击缩放
  try {
    // @ts-ignore
    map?.doubleClickZoom?.enable?.()
  } catch {}
  drawing.value = false
  unbindMapEvents()

  // 1) 移除尾部占位点（若存在）
  if (localPoints.value.length > 1) {
    localPoints.value.splice(localPoints.value.length - 1, 1)
  }

  // 2) 再次去重：若尾部两点重合（双击产生的重复），只保留一个
  const n = localPoints.value.length
  if (n >= 2) {
    const a = localPoints.value[n - 1]
    const b = localPoints.value[n - 2]
    if (almostEqual(a.latitude, b.latitude) && almostEqual(wrapLon(a.longitude), wrapLon(b.longitude))) {
      localPoints.value.splice(n - 1, 1)
    }
  }

  // 3) 触发完成/取消事件
  if (commit) emit('complete', localPoints.value)
  else emit('cancel')

  // 4) 结束后不高亮任何点
  selectedIndex.value = null
  emit('select', null)

  // 在双击结束绘制时，触发一次（防抖）外部同步
  if (commit) emitPointsDebounced()

  redraw()
}

function setPoints(points: RoutePoint[]) {
  syncingFromProps.value = true
  localPoints.value = normalize(points || [])
  redraw()
  nextTick(() => {
    syncingFromProps.value = false
    focusIndex(0)
  })
}

function insertAbove(i: number) {
  if (i <= 0 || i >= localPoints.value.length) return
  const prev = localPoints.value[i - 1]
  const cur = localPoints.value[i]
  const mid: RoutePoint = {
    latitude: round((prev.latitude + cur.latitude) / 2, 6),
    longitude: round((prev.longitude + cur.longitude) / 2, 6),
  }
  localPoints.value.splice(i, 0, mid)
  selectedIndex.value = i // 新插入点设为选中
  emit('select', i)
  redraw()
  emitPointsDebounced()
}

function deleteAt(i: number) {
  if (localPoints.value.length <= 1) return
  if (i < 0 || i >= localPoints.value.length) return
  localPoints.value.splice(i, 1)
  // 维护选中态
  if (selectedIndex.value != null) {
    if (localPoints.value.length === 0) selectedIndex.value = null
    else if (i <= selectedIndex.value) {
      selectedIndex.value = Math.max(0, Math.min(selectedIndex.value - 1, localPoints.value.length - 1))
    }
  }
  redraw()
  emitPointsDebounced()
}

function focusIndex(i: number) {
  const p = localPoints.value[i]
  if (!p) return
  try { if (map) map.easeTo({ center: [wrapLon(p.longitude), p.latitude], duration: 600 }) } catch {}
}

function selectIndex(i: number | null) {
  if (i == null) { selectedIndex.value = null; emit('select', null); redraw(); return }
  if (i < 0 || i >= localPoints.value.length) return
  selectedIndex.value = i
  emit('select', i)
  redraw()
}
function clearSelection() { selectIndex(null) }

defineExpose({ startDraw, stopDraw, setPoints, insertAbove, deleteAt, focusIndex, selectIndex, clearSelection, clearAll })
/**
 * 清理：清空点位与线并解绑地图事件（对外暴露）
 * - 退出绘制态并恢复双击缩放
 * - 解绑地图事件
 * - 清空点位/选中/Marker
 * - 将线的数据源置空
 */
function clearAll() {
  // 若处于绘制态，先恢复双击缩放
  if (drawing.value) {
    try {
      // @ts-ignore
      map?.doubleClickZoom?.enable?.()
    } catch {}
  }
  drawing.value = false
  // 清理地图交互事件
  try { unbindMapEvents() } catch {}
  // 关闭右键菜单（如有）
  try { hideCtx() } catch {}

  // 清空点位与选中状态
  localPoints.value = []
  selectedIndex.value = null
  emit('select', null)

  // 移除所有 Marker
  try { clearMarkers() } catch {}

  // 清空线数据源
  if (map) {
    try {
      const src: any = map.getSource(props.lineSourceId)
      if (src) src.setData({ type: 'FeatureCollection', features: [] })
    } catch {}
  }
}

/* ================================
 * 地图交互事件
 * ============================== */
function bindMapEvents() {
  if (!map) return
  map.on('click', onMapClick)
  map.on('mousemove', onMapMove)
  map.on('dblclick', onMapDblClick)
}
function unbindMapEvents() {
  if (!map) return
  map.off('click', onMapClick)
  map.off('mousemove', onMapMove)
  map.off('dblclick', onMapDblClick)
}

function onMapClick(e: any) {
  if (!drawing.value) return
  const { lng, lat } = e.lngLat
  const np = { latitude:lat, longitude: wrapLon(lng) }
  if (localPoints.value.length === 0) {
    localPoints.value.push(np)
    localPoints.value.push({ ...np })
  } else {
    // 把尾部占位点更新为点击位置，并再追加一个占位
    localPoints.value[localPoints.value.length - 1] = np
    localPoints.value.push({ ...np })
  }
  redraw()
}

function onMapMove(e: any) {
  if (!drawing.value) return
  if (localPoints.value.length < 1) return
  const { lng, lat } = e.lngLat
  localPoints.value[localPoints.value.length - 1] = { latitude:lat, longitude: wrapLon(lng) }
  redraw()
}

function onMapDblClick() { stopDraw(true) }

/* ================================
 * 重绘（线/点）
 * ============================== */
function redraw() {
  if (!map) return
  // 拖拽进行中：禁止全量重建（会 remove 正在拖拽的 Marker 实例），线的刷新由拖拽回调负责
  if (isDragging.value) return

  // 样式未就绪时延后
  // @ts-ignore
  if (map.isStyleLoaded && !map.isStyleLoaded()) { whenStyleReady(() => redraw()); return }

  // 更新折线数据源
  const fc = toLineFeatureCollection(localPoints.value)
  const src: any = map.getSource(props.lineSourceId)
  if (src) src.setData(fc)
  else ensureLineLayer(fc)

  // 重建 markers
  clearMarkers()
  const pts = localPoints.value.slice()
  // 绘制态下最后一个点是占位点，不生成 marker
  const end = drawing.value ? Math.max(pts.length - 1, 0) : pts.length
  for (let idx = 0; idx < end; idx++) {
    const p = pts[idx]
    const mk = createMarker(p, idx, selectedIndex.value === idx)
    markers.value.push(mk)
  }
}

function ensureLineLayer(data?: any) {
  if (!map) return
  // 样式未就绪时延后
  // @ts-ignore
  if (map.isStyleLoaded && !map.isStyleLoaded()) { whenStyleReady(() => ensureLineLayer(data)); return }
  if (!map.getSource(props.lineSourceId)) {
    map.addSource(props.lineSourceId, { type: 'geojson', data: data ?? toLineFeatureCollection([]) })
  }
  if (!map.getLayer(props.lineLayerId)) {
    map.addLayer({
      id: props.lineLayerId,
      type: 'line',
      source: props.lineSourceId,
      paint: {
        'line-color': '#000000',
        'line-width': 2.5,
        'line-join': 'round',
        'line-cap': 'round'
      }
    })
  } else {
    // 若已存在图层，确保样式更新为黑色航线
    map.setPaintProperty(props.lineLayerId, 'line-color', '#000000')
    map.setPaintProperty(props.lineLayerId, 'line-width', 2.5)
    map.setPaintProperty(props.lineLayerId, 'line-join', 'round' as any)
    map.setPaintProperty(props.lineLayerId, 'line-cap', 'round' as any)
  }
}

function clearMarkers() {
  if (!map) { markers.value = []; return }
  markers.value.forEach(m => { try { m.remove() } catch {} })
  markers.value = []
}

function createMarker(p: RoutePoint, idx: number, selected: boolean): Marker {
  const M = getMarkerCtor()
  if (!M) {
    // 构造函数不可用时跳过，返回空实现避免中断
    // @ts-ignore
    return {
      remove() {},
      getElement() { return document.createElement('div') },
      on() {},
      setLngLat() { return this },
      addTo() { return this }
    } as Marker
  }

  const draggable = props.markerDraggable && props.editable && !p.isPilot
  const el = document.createElement('div')
  el.className = 'self-marker'
  if (selected) el.classList.add('selected')
  el.dataset.index = String(idx)
  el.addEventListener('click', (ev) => {
    ev.stopPropagation()
    selectIndex(idx)
    focusIndex(idx)
  })
  // 在 Marker 上也监听 dblclick，解决“在点位上双击不会触发地图 dblclick”的问题
  el.addEventListener('dblclick', (ev) => {
    ev.preventDefault()
    ev.stopPropagation()
    if (drawing.value) stopDraw(true)
  })

  const mk: Marker = new M({ element: el, draggable })
    .setLngLat([wrapLon(p.longitude), p.latitude])
    .addTo(map)

  try {
    // 把外层 mapboxgl-marker 提到最上层
    const wrapper = mk.getElement() as HTMLElement
    wrapper.style.zIndex = '2000'
    // 兼容新版 API
    // @ts-ignore
    if (typeof mk.setZIndex === 'function') mk.setZIndex(2000)
  } catch {}

  if (draggable) {
    // 仅刷新折线数据源，避免全量重建
    const refreshLineOnly = () => {
      const fc = toLineFeatureCollection(localPoints.value)
      const src: any = map.getSource(props.lineSourceId)
      if (src) src.setData(fc)
    }

    const onDragUpdate = () => {
      const { lng, lat } = mk.getLngLat()
      localPoints.value[idx].latitude = lat
      localPoints.value[idx].longitude = wrapLon(lng)
      // 只刷新折线，不 redraw()，防止中断当前拖拽
      refreshLineOnly()

      // 拖拽时保持当前点为选中态
      if (selectedIndex.value !== idx) {
        selectedIndex.value = idx
        emit('select', idx)
      }
    }

    mk.on('dragstart', () => {
      isDragging.value = true
      // Ensure marker is selected and styled when drag starts
      if (selectedIndex.value !== idx) {
        selectedIndex.value = idx
        emit('select', idx)
      }
      // Remove 'selected' class from all markers before adding to this one
      markers.value.forEach(m => {
        try {
          m.getElement().classList.remove('selected')
        } catch {}
      })
      el.classList.add('selected')
      try { (el as HTMLElement).style.cursor = 'grabbing' } catch {}
    })

    mk.on('drag', onDragUpdate)

    mk.on('dragend', () => {
      onDragUpdate()               // 收尾再同步一次
      isDragging.value = false
      try { (el as HTMLElement).style.cursor = 'grab' } catch {}
      redraw()                     // 结束后做一次完整重绘，更新选中样式/顺序等
      emitPointsDebounced()
    })
  }

  return mk
}

/* ================================
 * 计算与几何工具
 * ============================== */
function normalize(list: RoutePoint[] = []): RoutePoint[] {
  return list.map((r) => ({
    isPilot: !!r.isPilot,
    latitude: Number(r.latitude) || 0,
    longitude: Number(r.longitude) || 0,
  }))
}

function toLineFeatureCollection(points: RoutePoint[]) {
  // 空/单点兜底
  if (!points || points.length === 0) {
    return { type: 'FeatureCollection', features: [] as any[] }
  }
  if (points.length === 1) {
    const p = points[0]
    const w = wrapLon(p.longitude)
    return {
      type: 'FeatureCollection',
      features: [{
        type: 'Feature',
        properties: {},
        geometry: { type: 'LineString', coordinates: [[w, p.latitude], [w + 1e-9, p.latitude]] }
      }]
    }
  }

  // 最短路径拆段（逐段决策，避免反向跨界走长边）
  const segments: number[][][] = []
  let curr: number[][] = []
  const EPS = 1e-12

  for (let i = 0; i < points.length - 1; i++) {
    const aLat = points[i].latitude
    const bLat = points[i + 1].latitude
    const aLon = wrapLon(points[i].longitude)
    const bLon = wrapLon(points[i + 1].longitude)

    if (curr.length === 0) curr.push([aLon, aLat])

    // 将 b 调整到与 a 的“最短世界拷贝”
    let bAdj = bLon
    const raw = bLon - aLon
    if (raw > 180) bAdj = bLon - 360
    else if (raw < -180) bAdj = bLon + 360

    // 不跨界：直接连（输出经度统一用 wrap 后的 bLon）
    if (bAdj <= 180 + EPS && bAdj >= -180 - EPS) {
      curr.push([bLon, bLat])
      continue
    }

    // 发生跨界：根据 bAdj 溢出的方向与 ±180 求交点，并分段
    if (bAdj > 180) {
      // 跨 +180：先到 +180，再从 -180 继续到 wrap(bLon)
      const denom = (bAdj - aLon)
      const t = Math.abs(denom) < EPS ? 0.5 : (180 - aLon) / denom
      // 以 Mercator y 做线性插值，避免投影后的折线出现“拐点”
      const yA = mercY(aLat)
      const yB = mercY(bLat)
      const yCross = yA + t * (yB - yA)
      const latCross = mercLat(yCross)
      curr.push([180, latCross])
      segments.push(curr)
      curr = []
      curr.push([-180, latCross])
      curr.push([wrapLon(bLon), bLat])
      continue
    }

    if (bAdj < -180) {
      // 跨 -180：先到 -180，再从 +180 继续到 wrap(bLon)
      const denom = (bAdj - aLon)
      const t = Math.abs(denom) < EPS ? 0.5 : (-180 - aLon) / denom
      // 以 Mercator y 做线性插值，避免投影后的折线出现“拐点”
      const yA = mercY(aLat)
      const yB = mercY(bLat)
      const yCross = yA + t * (yB - yA)
      const latCross = mercLat(yCross)
      curr.push([-180, latCross])
      segments.push(curr)
      curr = []
      curr.push([180, latCross])
      curr.push([wrapLon(bLon), bLat])
      continue
    }
  }

  if (curr.length) segments.push(curr)

  const geometry = segments.length > 1
      ? { type: 'MultiLineString', coordinates: segments }
      : { type: 'LineString', coordinates: segments[0] }

  return {
    type: 'FeatureCollection',
    features: [{ type: 'Feature', properties: {}, geometry } as any]
  }
}

function within180(lonU: number) { return lonU >= -180 && lonU <= 180 }
function wrapLon(longitude: number) {
  let x = Number(longitude) || 0
  while (x > 180) x -= 360
  while (x < -180) x += 360
  return x
}

// WebMercator 投影辅助：与 Mapbox 一致
function mercY(latDeg: number) {
  const phi = (latDeg * Math.PI) / 180
  return Math.log(Math.tan(Math.PI / 4 + phi / 2))
}
function mercLat(y: number) {
  const phi = 2 * Math.atan(Math.exp(y)) - Math.PI / 2
  return (phi * 180) / Math.PI
}

// 将经度序列“解包”为连续序列：相邻经度差选择最小跳变（±360 调整）
function unwrapLons(lons: number[]): number[] {
  if (!lons.length) return []
  const out: number[] = []
  out[0] = wrapLon(lons[0])
  for (let i = 1; i < lons.length; i++) {
    const raw = wrapLon(lons[i])
    const prev = out[i - 1]
    const cands = [raw, raw + 360, raw - 360]
    let best = cands[0]
    let bestDiff = Math.abs(best - prev)
    for (let k = 1; k < cands.length; k++) {
      const d = Math.abs(cands[k] - prev)
      if (d < bestDiff) { best = cands[k]; bestDiff = d }
    }
    out[i] = best
  }
  return out
}

function clonePoint(p: RoutePoint): RoutePoint { return { ...p } }
function round(n: number, p = 2) { return Math.round(n * 10 ** p) / 10 ** p }

function almostEqual(a: number, b: number, eps = 1e-6) { return Math.abs(a - b) <= eps }

/* ================================
 * 文本显示（度-分 + NESW）
 * ============================== */
function formatLat(v: number) {
  const dir = v >= 0 ? 'N' : 'S'
  const abs = Math.abs(v)
  const deg = Math.floor(abs)
  const min = (abs - deg) * 60
  return `${deg}° ${min.toFixed(2)}' ${dir}`
}
function formatLon(v: number) {
  const dir = v >= 0 ? 'E' : 'W'
  const abs = Math.abs(v)
  const deg = Math.floor(abs)
  const min = (abs - deg) * 60
  return `${deg}° ${min.toFixed(2)}' ${dir}`
}

/* ================================
 * 右键菜单（向上插入 / 删除）
 * ============================== */
const ctxMenu = ref<{show: boolean; x: number; y: number; index: number}>({ show: false, x: 0, y: 0, index: -1 })

function onRowContextmenu(row: RoutePoint, _column: any, event: MouseEvent) {
  event.preventDefault()
  event.stopPropagation()
  const idx = localPoints.value.indexOf(row)
  if (idx < 0) return
  // 估算菜单尺寸，用于防止超出视口（与样式保持一致）
  const MENU_W = 160
  const MENU_H = 92
  const vw = window.innerWidth
  const vh = window.innerHeight
  const x = Math.max(0, Math.min(event.clientX, vw - MENU_W))
  const y = Math.max(0, Math.min(event.clientY, vh - MENU_H))
  ctxMenu.value = { show: true, x, y, index: idx }
  bindCtxOutside()
}

function ctxInsertAbove() {
  const i = ctxMenu.value.index
  hideCtx()
  if (i <= 0) return // 首行不可插
  insertAbove(i)
}

function ctxDelete() {
  const i = ctxMenu.value.index
  hideCtx()
  deleteAt(i)
}

function hideCtx() {
  ctxMenu.value.show = false
  unbindCtxOutside()
}

function onDocClick() { hideCtx() }
function bindCtxOutside() { document.addEventListener('click', onDocClick, { once: true }) }
function unbindCtxOutside() { document.removeEventListener('click', onDocClick) }

/* ================================
 * 统一清理
 * ============================== */
function cleanup() {
  try { unbindMapEvents() } catch {}
  try { clearMarkers() } catch {}
}
</script>

<template>
  <section class="map-route-wrapper">
    <div class="map-route-drawer" />

    <!-- 自定义点位表格（仅保留序号与位置显示） -->
    <el-table
        :data="localPoints"
        v-if="localPoints.length > 0"
        class="point-table"
        height="250"
        :header-cell-style="{ textAlign: 'center' }"
        :cell-style="{ textAlign: 'center' }"
        @row-contextmenu="onRowContextmenu"
        @row-click="(_, __, rowIndex) => selectIndex(rowIndex as number)"
        @contextmenu.prevent
    >
      <el-table-column label="序号" width="70" align="center">
        <template #default="{ $index }">{{ $index + 1 }}</template>
      </el-table-column>
      <el-table-column label="位置" min-width="280" align="center">
        <template #default="{ $index }">
          <div class="pos" :class="{ 'is-selected': selectedIndex === $index }">
            <span>{{ formatLat(localPoints[$index].latitude) }}</span>
            <span class="pos-split">,</span>
            <span>{{ formatLon(localPoints[$index].longitude) }}</span>
          </div>
        </template>
      </el-table-column>
    </el-table>

    <!-- 右键菜单（参照图二样式），Teleport 到 body 防止被裁剪/遮挡 -->
    <teleport to="body">
      <div v-if="ctxMenu.show" class="ctx-menu" :style="{ left: ctxMenu.x + 'px', top: ctxMenu.y + 'px' }">
        <div class="ctx-item" :class="{ disabled: ctxMenu.index <= 0 }" @click="ctxInsertAbove">向上插入</div>
        <div class="ctx-divider"></div>
        <div class="ctx-item" :class="{ disabled: localPoints.length <= 1 }" @click="ctxDelete">删除</div>
      </div>
    </teleport>
  </section>
</template>

<style scoped>
/* 仅保留与点位展示/Marker 高亮相关的样式 */
.map-route-wrapper { display: flex; flex-direction: column; gap: 8px; }
.map-route-drawer { /* 该组件自身不渲染任何地图 UI，仅负责地图库资源 */ }

/* ElTable 深蓝背景、白字、hover/选中高亮样式 for .point-table */
.point-table {
  --el-table-bg-color: transparent;
  --el-table-tr-bg-color: #093d6d;
  --el-table-header-bg-color: #132e7a;
  --el-table-header-text-color:#FFFFFF;
  --el-table-row-hover-bg-color: #0c4b86;
  --el-table-border-color: rgba(255,255,255,0.17);
  --el-table-text-color: rgba(255,255,255,0.80);
}
.point-table th, .point-table td {
  height: 20px;
  line-height: 20px;
  padding: 0 !important;
  font-size: 12px;
}
.point-table thead {
  background-color: #093d6d;
}
.point-table thead th {
  background-color: #093d6d !important;
  color: #ffffff !important;
  font-weight: 700;
  border-bottom: 1px solid rgba(255,255,255,0.17);
}
.point-table thead .cell {
  font-weight: 700;
  color: #ffffff;
}
.point-table .el-table__row.current-row>td {
  background: #0e589c !important;
}

.point-table .pos { display: flex; align-items: center;justify-content: center; gap: 6px; font-variant-numeric: tabular-nums; }
.point-table .pos-split { padding: 0 4px; color: #aeb8c0; }
.point-table .pos.is-selected { color: #FF5B3EFF; font-weight: 600; }

.map-route-wrapper :deep(.mapboxgl-marker) {
  z-index: 2000;
  pointer-events: auto;
}

.ctx-menu {
  position: fixed;
  z-index: 10000;
  min-width: 128px;
  background: #ffffff;
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: 4px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08);
  padding: 4px 0;
  color: #303133;
}
.ctx-item {
  padding: 8px 14px;
  font-size: 13px;
  line-height: 20px;
  cursor: pointer;
  user-select: none;
}
.ctx-item:hover { background: #f5f7fa; }
.ctx-item.disabled { color: #c0c4cc; cursor: not-allowed; pointer-events: none; background: transparent; }
.ctx-divider { height: 1px; margin: 4px 0; background: rgba(0,0,0,0.06); }
</style>

<style>
.self-marker {
  width: 10px;
  height: 10px;
  background-color: #ffffff; /* 白色点位，贴近图二风格 */
  border-radius: 50%;
  border: 2px solid #1f2d3d; /* 深色描边，通用底图可见性更好 */
  box-shadow: 0 0 6px rgba(0,0,0,0.35);
  cursor: grab;
  pointer-events: auto; /* 确保可点击 */
  &:hover { transform: scale(1.08); }

}

.self-marker.selected {
  box-shadow: 0 0 10px rgba(62,255,229,0.75);
  border-color: #ff5b3e;
  transform: scale(1.1);
}

</style>
